package com.momoauth.common.security.interceptor;

import com.momoauth.common.security.annotation.RequiresRoles;
import com.momoauth.common.security.dao.RoleDao;
import com.momoauth.common.core.exception.GlobalException;
import com.momoauth.common.security.entity.Role;
import com.momoauth.common.security.utils.JwtUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class AuthInterceptor implements HandlerInterceptor {

    @Resource
    RoleDao roleDao;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        RequiresRoles annotation = null;
        if(handler instanceof HandlerMethod method) {
            // 获取方法上注解的值
            // annotation = method.getMethodAnnotation(RequiresRoles.class);
            // 获取类上注解的值
            annotation = method.getMethod().getDeclaringClass().getAnnotation(RequiresRoles.class);
            if (annotation != null) {
                // 判断是否拥有角色权限
                if (!roleJudgment(annotation)) {
                    throw new GlobalException(50001,"很抱歉，角色权限不足。");
                }
            }
        }
        return true;
    }

    /**
     * 角色判断
     * @param requiresRoles
     * @return
     */
    private boolean roleJudgment(RequiresRoles requiresRoles) {
        // 值为空时返回错误信息
        if (requiresRoles.value().length == 0) {
            throw new GlobalException(50000,"@RequiresRoles注解内请填写合法的值。");
        }
        HttpServletRequest request = currentRequest();
        String token = request.getHeader("Authorization");
        // 非空判断
        if (token != null && !token.isEmpty()) {
            // 从头部Token解析出用户ID
            Long userId = Long.parseLong(JwtUtil.getClaimName(token, "userId"));
            List<String> list = new ArrayList<>();
            // 根据用户ID取出该用户拥有的角色列表
            List<Role> allRoleList = roleDao.getRoleNameList(userId);
            // 查询不到任何角色信息时直接抛出错误
            if (allRoleList.size() == 0) {
                throw new GlobalException(50001,"该用户不拥有任何角色。");
            }
            // 把角色名称取出来丢进另一个List
            for (Role role : allRoleList) {
                list.add(role.getRoleName());
            }
            String[] roleList = list.toArray(new String[list.size()]);
            // 根据枚举常量判断结果是否吻合
            switch (requiresRoles.logical()) {
                case OR:
                    for (String roleName : requiresRoles.value()) {
                        if (Arrays.asList(roleList).contains(roleName)) {
                            return true;
                        }
                    }
                    break;
                case AND:
                    List<String> duplicateList = new ArrayList<>();
                    // 循环取出相同的元素重新组合
                    for (int i = 0; i < roleList.length; i++) {
                        if (ArrayUtils.contains(requiresRoles.value(), roleList[i])) {
                            duplicateList.add(roleList[i]); //相同的元素
                        }
                    }
                    String[] identicalArr = duplicateList.toArray(new String[duplicateList.size()]);
                    // 判断两个数组是否相同
                    if (Arrays.equals(requiresRoles.value(), identicalArr)) {
                        return true;
                    }
                    break;
            }
        }
        return false;
    }

    private HttpServletRequest currentRequest() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return Optional.ofNullable(servletRequestAttributes).map(ServletRequestAttributes::getRequest).orElse(null);
    }
}
